#!/usr/bin/env python

import ConfigParser
import os
import sys
import time
from threading import Thread
from optparse import OptionParser
import subprocess 

def getServers(sectionname) :
    """
    Returns a list of hostnames for a given sectionname in the configuration file. 
    
    If the section was a group, we'll traverse the configuration further
    """
    servers = [] 
    if not config.has_section(sectionname): raise RuntimeError('Server or group ' + sectionname + ' not found in configuration') 

    # Checking if the configuration file has a 'host' section 
    if config.has_option(sectionname,'host'):
        servers.append(config.get(sectionname,'host'))

    # Checking if the configuration has a 'group' section

    if (config.has_option(sectionname,'group')):
        grouplist = config.get(sectionname,'group').split(',')
        for i in grouplist : 
            servers+=getServers(i)

    return servers

class FloepCommand(Thread) :
    """
    This is our worker process

    It's responsible for executing the command, and storing the results afterwards
    """
    def __init__ (self,host,command,options) :
        Thread.__init__(self)
        self.host = host
        self.command = command
        self.options = options

    def run(self) :
        commandstring = self.options.command % {'host' : self.host, 'command' : self.command}
        process = subprocess.Popen(commandstring,stderr=subprocess.STDOUT,stdout=subprocess.PIPE,shell=True).stdout
        self.result = process.read() 

def readConfiguration() :

    global commandTemplate
    global defaultGroup
    config.read(os.path.dirname(sys.argv[0]) + '/floep.cfg')
    config.read('floep.cfg')

    if config.has_option('settings','commandTemplate'):
        commandTemplate = config.get('settings','commandTemplate')
    else:
        commandTemplate = 'ssh %(host)s %(command)s'

    if config.has_option('settings','defaultGroup'):
        defaultGroup = config.get('settings','defaultGroup')
    else:
        defaultGroup = 'all'

def parseOptions() :
    global commandTemplate
    parser = OptionParser(
        version="floep 0.1",
        description="Executes a command on multiple hosts"
    )
    parser.disable_interspersed_args()
    parser.add_option(
        '-g','--group',
        default=defaultGroup, 
        type="string", 
        help="Execute command on group", 
        dest="group")

    parser.add_option(
        '-q','--quiet',
        action="store_false", 
        help="Display only server output", 
        dest="verbose", 
        default=True)

    parser.add_option(
        '-c','--commandTemplate',
        default=commandTemplate, 
        dest="command", 
        help="The commandline to execute for each host")

    options,args = parser.parse_args()
    if options.verbose: 
        print "floep 0.1" 

    if not args :
        parser.error('no command given')

    command = " ".join(args)

    return options,command


def runCommandOnServers(servers,command,options) :

    threadlist = []
    for server in servers :
      current = FloepCommand(server,command,options)
      threadlist.append(current)
      current.start()

    while threadlist : 
        for server in threadlist :

            if not server.isAlive():
                server.join()
                if options.verbose: 
                    print "Result from", server.host, ":"
                print server.result,
                if options.verbose: 
                    print ""
                threadlist.remove(server)
                break

        else :
            time.sleep(0.010)


commandTemplate = ''
config = ConfigParser.RawConfigParser()

def main() :
    readConfiguration()
    options,command = parseOptions()
    servers = getServers(options.group)
    runCommandOnServers(servers,command,options)

if __name__ == "__main__" :
    main()

